using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

using Sustainalytics.OAuth.Services.Common;
using Sustainalytics.Portfolios.BL;
using Sustainalytics.Portfolios.BL.Exceptions;
using Sustainalytics.Portfolios.Service.ErrorCodes;
using Sustainalytics.Portfolios.Service.Models;
using Sustainalytics.Utils;

using WebApi.OutputCache.V2;
using System.Web.Http.Cors;
using CarteConnection;
using System.Configuration.Abstractions;
using MongoDB.Driver;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.MongoDB.Extensions;
using Sustainalytics.DataLayer.ConnectionStrings;
using Sustainalytics.Entities.ConnectionStrings;

namespace Sustainalytics.Portfolios.Service.Controllers
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/v1/portfolios/{portfolioId}/details")]
    public class PortfoliosDetailsController : ApiController
    {
        private readonly IPortfolioReadHandler _portfolioReadHandler;

        private readonly ICustomPortfolioUpdateHandler _portfolioUpdateHandler;

        private readonly PortfolioUserClaimsProcessor _userClaimsProcessor;

        private readonly IMongoCollection<CorporateData> _corporateDataCollection;


        private readonly string _cartePath;
        private readonly string _carteParameter;

        public PortfoliosDetailsController(
            IPortfolioReadHandler portfolioReadHandlerManagement,
            ICustomPortfolioUpdateHandler portfolioUpdateHandler,
            PortfolioUserClaimsProcessor userClaimsProcessor)
        {
            this._portfolioReadHandler = portfolioReadHandlerManagement;
            this._portfolioUpdateHandler = portfolioUpdateHandler;
            this._userClaimsProcessor = userClaimsProcessor;
            this._cartePath = ConfigurationManager.Instance.AppSettings["CartePath"];
            this._carteParameter = ConfigurationManager.Instance.AppSettings["CarteParameter"];

            var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ClientPlatform"].ConnectionString;
            var connectionStringsManager = ConnectionStringsManager.GetInstance(ConnectionStringsRepository.GetInstance(connectionString));
            var corporateDataConnectionString = connectionStringsManager.GetConnectionString(ConnectionStringType.ClientDW);

            _corporateDataCollection = MongoFactory.GetCollectionFromDatabase<CorporateData>(corporateDataConnectionString);
        }

        [Route("")]
        public HttpResponseMessage Get(
            Guid portfolioId,
            [FromUri] ListOptions listOptions,
            string userId /* kept here just for caching*/)
        {
            var userClaims = _userClaimsProcessor.GetPortfolioClaims();

            int count;

            var portfolioDetails = this._portfolioReadHandler.GetPortfolioSecurities(
                portfolioId,
                out count,
                userClaims,
                listOptions.Skip,
                listOptions.Take,
                listOptions.OrderBy,
                listOptions.OrderAscending);

            IEnumerable<PortfolioDetailsBaseDto> potfolioDetailsList;
            if (listOptions.IncludeAllFields)
            {
                potfolioDetailsList = portfolioDetails.Select(p => p.ToDto()).Where(p => !p.IsInvalidMatch).ToList();
            }
            else
            {
                potfolioDetailsList = portfolioDetails.Select(p => p.ToBaseDto()).Where(p => !p.IsInvalidMatch).ToList();
            }

            if (!listOptions.GetCount)
            {
                return this.Request.CreateResponse(HttpStatusCode.OK, potfolioDetailsList);
            }

            var companiesIds = potfolioDetailsList.Select(x => x.CompanyId);

            var builderCorporateData = Builders<CorporateData>.Filter;
            var filter = (builderCorporateData.In(x => x.Id, companiesIds));

            var companiesCollection = _corporateDataCollection
                .Find(filter)
                .Project<CorporateData>(Builders<CorporateData>.Projection
                    .Include(x => x.Id)
                    .Include(x => x.MainExchange)
                    .Include(x => x.SubPeerGroup));

            var companiesList =  companiesCollection
               //.Skip(listOptions.Skip)
               //.Limit(listOptions.Take)
               .ToList();

            var companiesExchangeAndIndustry = companiesList.Select(x => new { id= x.Id,exchange = x.MainExchange, subindustry = x.SubPeerGroup });

            return this.Request.CreateResponse(
                HttpStatusCode.OK,
                new
                {
                    portfolioDetails = potfolioDetailsList,
                    PortfolioCompanyCount = count,
                    portfolioAdditionalDetails = companiesExchangeAndIndustry
                });
        }

        [Route("{companyId}")]
        public HttpResponseMessage Post(Guid portfolioId, Guid companyId)
        {
            if (portfolioId == Guid.Empty && companyId == Guid.Empty)
            {
                return this.Request.CreateResponse(HttpStatusCode.BadRequest);
            }

            try
            {
                var userClaims = _userClaimsProcessor.GetPortfolioClaims();

                var exists = this._portfolioUpdateHandler.AddCompanyToPortfolio(portfolioId, companyId, userClaims);

                // invalidate cache for parent resource 
                var cache = this.Configuration.CacheOutputConfiguration().GetCacheOutputProvider(this.Request);
                cache.RemoveStartsWith(
                    this.Configuration.CacheOutputConfiguration().MakeBaseCachekey("Portfolios", "Get"));

                if (exists.SecuritiesExists)
                {
                    return this.Request.CreateResponse(
                        HttpStatusCode.OK,
                        new { WarningCode = PortfoliosErrorCodes.PortfolioCompanyExistsInPortfolio });
                }

                if (exists.SavedWithErrors)
                {
                    return this.Request.CreateResponse(
                        HttpStatusCode.OK,
                        new { WarningCode = PortfoliosErrorCodes.PortfolioCompanyInvalidMatch });
                }

                return this.Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (PortfolioModificationUnauthorizedException)
            {
                return this.Request.CreateResponse(
                    HttpStatusCode.Forbidden,
                    new { ErrorCode = PortfoliosErrorCodes.PortfolioCompanyInsertUnauthorised });
            }
        }

        [Route("{id}")]
        public HttpResponseMessage Delete(Guid portfolioId, Guid id)
        {
            PortfolioUserClaims userClaims = _userClaimsProcessor.GetPortfolioClaims();

            this._portfolioUpdateHandler.DeletePortfolioSecurity(portfolioId, id, userClaims);

            // invalidate cache for parent resource (in this case we need it because CompaniesDto contains number of indicators)
            var cache = this.Configuration.CacheOutputConfiguration().GetCacheOutputProvider(this.Request);
            cache.RemoveStartsWith(
                this.Configuration.CacheOutputConfiguration().MakeBaseCachekey("Portfolios", "Get"));

            //sending portfolio id to link: CartePath"
            var carteConnection = new PortfolioUpdate(portfolioId, "Delete Portfolio Security",_cartePath, _carteParameter);
            carteConnection.SendPortfolioIdToCarte();

            return this.Request.CreateResponse(HttpStatusCode.OK);
        }
    }
}